#include "EnvMapGenerator.h"

#include "gameobjects/GameObject.h"

using namespace Ogre;

EnvMapGenerator::EnvMapGenerator(Root* root, SceneManager* smgr) :
	Component("EnvMapGenerator"),
	mNode(0),
	mTexName("EnvMapTex"+StringConverter::toString(howMany())),
	mSceneMgr(smgr)
//		mCamera(smgr->createCamera("EnvCamera"))
{
	setup();

	root->addFrameListener(this);
}


EnvMapGenerator::EnvMapGenerator(Root* root, SceneManager* smgr, ComponentContainer* c) :
	Component(c),
	mNode(0),
	mTexName("EnvMapTex"+StringConverter::toString(howMany())),
	mSceneMgr(smgr)
//		mCamera(smgr->createCamera("EnvCamera"))
{
	setup();

	root->addFrameListener(this);
}

EnvMapGenerator::~EnvMapGenerator()
{
	clean();
}


bool EnvMapGenerator::frameStarted(const FrameEvent& evt)
{
//	if(dt > 0)
//		dt -= evt.timeSinceLastFrame;
//	else {
//		dt = .5f;
//	}

	dt = evt.timeSinceLastFrame;

	return true;
}



void EnvMapGenerator::postRenderTargetUpdate(const RenderTargetEvent& evt)
{
//	if (mNode)
	mNode->setVisible(true);
	//mSphereNode->setVisible(false);
//	mTimer->reset();
}

void EnvMapGenerator::preRenderTargetUpdate(const RenderTargetEvent& evt)
{
//	if(mTimer->getMilliseconds()%2==0) return;

	mCamera->setPosition(mNode->getPosition());
	mNode->setVisible(false);


	// point the camera in the right direction based on which face of the cubemap this is
	mCamera->setOrientation(Quaternion::IDENTITY);
	if (evt.source == mTargets[0]) mCamera->yaw(Degree(-90));
	else if (evt.source == mTargets[1]) mCamera->yaw(Degree(90));
	else if (evt.source == mTargets[2]) mCamera->pitch(Degree(90));
	else if (evt.source == mTargets[3]) mCamera->pitch(Degree(-90));
	else if (evt.source == mTargets[5]) mCamera->yaw(Degree(180));

//	mCamera->setPosition(
//	    mNode->getPosition() +
//	    mNode->getAttachedObject(0)->getBoundingRadius() *
//	    mCamera->getOrientation() * Vector3::UNIT_Z
//	);

//			else if (evt.source == mSphereTarget) {
//				//mCamera->setPosition(Vector3::UNIT_Z);
//				mCamera->setPosition(Vector3::UNIT_Y);
//				mCamera->pitch(Degree(90));
//				mSphereNode->setVisible(true);
//			}


	Viewport* vp = evt.source->getViewport(0);
	vp->clear();
	vp->update();


//	mTimer->reset();
//	dt = .5f;

}


void EnvMapGenerator::setup()
{
	mTimer = new Ogre::Timer();

	// create the camera used to render to our cubemap
	mCamera = mSceneMgr->createCamera("EnvMapCamera"+StringConverter::toString(howMany()));
	mCamera->setFOVy(Degree(90));
	mCamera->setAspectRatio(1);
	mCamera->setFixedYawAxis(false);
	mCamera->setNearClipDistance(.1);
	mCamera->setFarClipDistance(10000);


	GameObject* go = dynamic_cast<GameObject*>(mContainer);
	assert(go!=NULL);

	mNode = go->getProperty<SceneNode*>("scenenode");
	assert(mNode!=NULL);

	//

	/*
	TexturePtr tex = TextureManager::getSingleton().createManual("dynenvmap",
	                 ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
	                 TEX_TYPE_2D ,
	                 256, 256,
	                 0,
	                 PF_R8G8B8, TU_RENDERTARGET);
	mPlaneTarget = tex->getBuffer(0)->getRenderTarget();

	mPlaneTarget->addViewport(mCamera);
	mPlaneTarget->getViewport(0)->setOverlaysEnabled(false);
	mPlaneTarget->getViewport(0)->setClearEveryFrame(true);
	mPlaneTarget->addListener(this);
	*/

	const int SIZE = 256;

	// create our dynamic cube map texture
//	TexturePtr tex = Ogre::TextureManager::getSingleton().createManual(
	mTex = Ogre::TextureManager::getSingleton().createManual(
	           mTexName,
	           ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
	           TEX_TYPE_CUBE_MAP,
	           SIZE, SIZE,
	           0,
	           PF_R8G8B8,
	           TU_RENDERTARGET);

	// assign our camera to all 6 render targets of the texture (1 for each direction)
	for (unsigned int i = 0; i < 6; i++)
		{
			mTargets[i] = mTex->getBuffer(i)->getRenderTarget();
			Viewport* vp = mTargets[i]->addViewport(mCamera);
			vp->setOverlaysEnabled(false);
//			vp->setMaterialScheme("Low");
			vp->setMaterialScheme("Medium");
			vp->setShadowsEnabled(false);
			vp->setSkiesEnabled(true);
			vp->setClearEveryFrame(false);
			vp->setAutoUpdated(false);

			mTargets[i]->addListener(this);
		}

//	MaterialPtr base = MaterialManager::getSingleton().getDefaultSettings();
//	MaterialPtr base = MaterialManager::getSingleton().getByName("BaseWhiteNoLighting");
	MaterialPtr base = MaterialManager::getSingleton().getByName("base_dyncubicmap_material");

	String matName = "EnvMapMat"+StringConverter::toString(howMany());

	MaterialPtr mat = base->clone(matName);
	TextureUnitState *tu = mat->getTechnique(0)->getPass(0)->createTextureUnitState();

//	tu->setTextureName(mTexName);
	tu->setCubicTextureName(mTexName, true);
	tu->setEnvironmentMap(true, TextureUnitState::ENV_REFLECTION);
	tu->setName("envMap");
	tu->setColourOperation(Ogre::LBO_ADD);

	Entity* ent = go->getProperty<Ogre::Entity*>("entity");
	ent->setMaterial(mat);


//		WorkQueue* wq = Root::getSingleton().getWorkQueue();
//		mWorkQueueChannel = wq->getChannel("EnvMapGenerator");
//		wq->addRequestHandler(mWorkQueueChannel, this);
//		wq->addResponseHandler(mWorkQueueChannel, this);


	/*
	// create our dynamic sphere map texture
	TexturePtr tex2 = TextureManager::getSingleton().createManual("dynspheremap",
	                 ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
	                 TEX_TYPE_2D ,
	                 128, 128,
	                 0,
	                 PF_R8G8B8, TU_RENDERTARGET);

	mSphereTarget = tex2->getBuffer(0)->getRenderTarget();

	mSphereTarget->addViewport(mCamera);
	mSphereTarget->getViewport(0)->setOverlaysEnabled(false);
	mSphereTarget->getViewport(0)->setClearEveryFrame(true);
	mSphereTarget->addListener(this);

	mSphereNode = mSceneMgr->createSceneNode("SphereNode");
	mSphereEnt = mSceneMgr->createEntity("Sphere", "Sphere.mesh");
	mSphereEnt->setMaterialName("DynCubeMap");
	mSphereNode->attachObject(mSphereEnt);
	mSphereNode->setVisible(false);
	*/
}

void EnvMapGenerator::clean()
{
//	TextureManager::getSingleton().remove("dyncubemap");
	TextureManager::getSingleton().remove(mTexName);

//		WorkQueue* wq = Root::getSingleton().getWorkQueue();
//		wq->removeRequestHandler(mWorkQueueChannel, this);
//		wq->removeResponseHandler(mWorkQueueChannel, this);

}
